package driver

import (
	"context"
	"log/slog"
	"slices"

	"gitee.com/gousing/cache/internal"
	"github.com/redis/go-redis/v9"
)

// redisTags 标签集合, 用于快速查找带有相同标签的缓存项
//   - 同时支持存储两种映射关系:
//   - tag到keys 集合(tag=>[]keys), 此时key为缓存tag，items元素为key集合
//   - key到tags 集合(key=>[]tags), 此时key为缓存key，items元素为tag集合
//   - Key => redisTagPre+"keys" 所有key的集合
//   - Key => redisTagPre+"items:key" 指定key的所有items集合
type redisTags struct {
	client      redis.UniversalClient
	nameIndex   string // 所有key或tag的集合
	itemsPrefix string // 所有items的集合前缀
	emptySlice  []string
}

var redisTagCtx = context.Background()

func newRedisTags(client redis.UniversalClient, prefixName string) *redisTags {
	rt := &redisTags{
		client:      client,
		nameIndex:   prefixName + "index",
		itemsPrefix: prefixName + "items:",
		emptySlice:  []string{},
	}
	// 初始化 Tags 集合键名索引, 不存在时创建, 并设置过期时间为0, 即永久有效
	if typ, err := client.Type(redisTagCtx, rt.nameIndex).Result(); err != nil || typ != "set" {
		// 可能由于键名已存在但不是集合类型, 例如是字符串等其他类型导致的错误
		// 删除后再试一次
		if _, err := client.Del(redisTagCtx, rt.nameIndex).Result(); err != nil {
			internal.GetLogger().Error("cache: newRedisTags init error", slog.String("nameIndex", rt.nameIndex), slog.String("error", err.Error()))
		} else if _, err := client.SAdd(redisTagCtx, rt.nameIndex, rt.nameIndex).Result(); err != nil {
			internal.GetLogger().Error("cache: newRedisTags init error", slog.String("nameIndex", rt.nameIndex), slog.String("error", err.Error()))
		} else {
			client.SRem(redisTagCtx, rt.nameIndex, rt.nameIndex)
		}
	}
	return rt
}

func (t *redisTags) Size() int64 {
	keys, err := t.client.SMembers(redisTagCtx, t.nameIndex).Result()
	if err != nil || len(keys) == 0 {
		return 0
	}
	var count int64 = 0
	for _, key := range keys {
		count += t.client.SCard(redisTagCtx, t.itemsPrefix+key).Val()
	}
	return count
}

func (t *redisTags) Has(key string) bool {
	if key == "" {
		return false
	}
	// 6.2.0 版本后才支持 SMIsMember 命令(多个KEY)，否则使用 SIsMember命令(单个KEY) 判断是否存在
	return t.client.SIsMember(redisTagCtx, t.nameIndex, key).Val()
}

func (t *redisTags) GetKeys() []string {
	keys, err := t.client.SMembers(redisTagCtx, t.nameIndex).Result()
	if err != nil || len(keys) == 0 {
		return t.emptySlice[:0]
	}
	// 排序返回结果
	slices.Sort(keys)
	return keys
}
func (t *redisTags) GetItems(key string) []string {
	items, err := t.client.SMembers(redisTagCtx, t.itemsPrefix+key).Result()
	if err != nil || len(items) == 0 {
		return t.emptySlice[:0]
	}
	// 排序返回结果
	slices.Sort(items)
	return items
}
func (t *redisTags) HasKV(key, item string) bool {
	if key == "" || item == "" {
		return false
	}
	return t.client.SIsMember(redisTagCtx, t.itemsPrefix+key, item).Val()
}

func (t *redisTags) AddKV(key string, items ...string) {
	if key == "" || len(items) == 0 {
		return
	}
	// 存储到 keys集合
	t.client.SAdd(redisTagCtx, t.nameIndex, key)
	// 存储到 key => items 集合
	members := make([]any, len(items))
	for i, v := range items {
		members[i] = v
	}
	t.client.SAdd(redisTagCtx, t.itemsPrefix+key, members...)
}
func (t *redisTags) Remove(key string) {
	if key == "" {
		return
	}
	t.client.SRem(redisTagCtx, t.nameIndex, key)
	t.client.Del(redisTagCtx, t.itemsPrefix+key)
}
func (t *redisTags) RemoveKV(key string, items ...string) {
	if key == "" || len(items) == 0 {
		return
	}
	members := make([]interface{}, len(items))
	for i, v := range items {
		members[i] = v
	}
	t.client.SRem(redisTagCtx, t.itemsPrefix+key, members...)
}
func (t *redisTags) Clear() {
	keys, err := t.client.SMembers(redisTagCtx, t.nameIndex).Result()
	t.client.Del(redisTagCtx, t.nameIndex)
	if err != nil || len(keys) == 0 {
		return
	}
	for _, key := range keys {
		t.client.Del(redisTagCtx, t.itemsPrefix+key)
	}
}
